package com.atguigu.tingshu.search.service.impl;

import co.elastic.clients.elasticsearch.ElasticsearchClient;
import co.elastic.clients.elasticsearch._types.FieldValue;
import co.elastic.clients.elasticsearch._types.SortOrder;
import co.elastic.clients.elasticsearch._types.aggregations.Aggregate;
import co.elastic.clients.elasticsearch._types.query_dsl.BoolQuery;
import co.elastic.clients.elasticsearch._types.query_dsl.Query;
import co.elastic.clients.elasticsearch.core.SearchRequest;
import co.elastic.clients.elasticsearch.core.SearchResponse;
import co.elastic.clients.elasticsearch.core.search.Hit;
import co.elastic.clients.elasticsearch.core.search.Suggestion;
import co.elastic.clients.json.JsonData;
import com.alibaba.fastjson.JSON;
import com.atguigu.tingshu.album.client.AlbumInfoFeignClient;
import com.atguigu.tingshu.album.client.CategoryFeignClient;
import com.atguigu.tingshu.common.constant.RedisConstant;
import com.atguigu.tingshu.common.execption.GuiguException;
import com.atguigu.tingshu.common.result.Result;
import com.atguigu.tingshu.common.util.PinYinUtils;
import com.atguigu.tingshu.model.album.*;
import com.atguigu.tingshu.model.search.AlbumInfoIndex;
import com.atguigu.tingshu.model.search.AttributeValueIndex;
import com.atguigu.tingshu.model.search.SuggestIndex;
import com.atguigu.tingshu.query.search.AlbumIndexQuery;
import com.atguigu.tingshu.search.repository.AlbumIndexRepository;
import com.atguigu.tingshu.search.repository.SuggestIndexRepository;
import com.atguigu.tingshu.search.service.SearchService;
import com.atguigu.tingshu.user.client.UserInfoFeignClient;
import com.atguigu.tingshu.vo.search.AlbumInfoIndexVo;
import com.atguigu.tingshu.vo.search.AlbumSearchResponseVo;
import com.atguigu.tingshu.vo.user.UserInfoVo;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.cloud.context.config.annotation.RefreshScope;
import org.springframework.data.elasticsearch.core.suggest.Completion;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;
import org.springframework.util.CollectionUtils;
import org.springframework.util.StringUtils;

import java.io.IOException;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.function.Function;
import java.util.stream.Collectors;


@Slf4j
@Service
@SuppressWarnings({"all"})
public class SearchServiceImpl implements SearchService {
    @Autowired
    private AlbumInfoFeignClient albumInfoFeignClient;

    @Autowired
    private CategoryFeignClient categoryFeignClient;

    @Autowired
    private UserInfoFeignClient userInfoFeignClient;

    @Autowired
    private AlbumIndexRepository albumIndexRepository;

    @Autowired
    private ThreadPoolExecutor threadPoolExecutor;

    @Autowired
    private ElasticsearchClient elasticsearchClient;

    @Autowired
    private SuggestIndexRepository suggestIndexRepository;

    @Value("${ranking.dimension.array}")
    private String rankingDimensionArray;

    @Autowired
    private RedisTemplate redisTemplate;

    /**
     * 专辑上架
     * @param albumId
     */
    @Override
    public void upperAlbum(Long albumId) {
        //创建AlbumInfoIndex对象
        AlbumInfoIndex albumInfoIndex = new AlbumInfoIndex();


        //使用多线程异步查询数据
        CompletableFuture<AlbumInfo> albumInfoCompletableFuture = CompletableFuture.supplyAsync(() -> {
            //因为后边查询三级分类id是还需要用到这步的albuminfo，所以选择supplyAsync提供返回值的供后边使用
            //赋值album_info表数据
            Result<AlbumInfo> albumInfoResult = albumInfoFeignClient.getAlbumInfo(albumId);
            //如果 albumInfoResult 为 null，则会抛出 IllegalArgumentException 异常，异常信息为第二个参数指定的字符串（"查询专辑信息失败"）
            Assert.notNull(albumInfoResult, "查询专辑信息失败");
            AlbumInfo albumInfo = albumInfoResult.getData();
            Assert.notNull(albumInfo, "查询专辑信息失败");
            BeanUtils.copyProperties(albumInfo, albumInfoIndex);

            //在获取专辑信息时，已经查出了属性值信息，所以在albuminfo这里拿属性值，获取属性与属性值信息
            List<AlbumAttributeValue> albumAttributeValueVoList = albumInfo.getAlbumAttributeValueVoList();
            //判断
            if(!CollectionUtils.isEmpty(albumAttributeValueVoList)){
                /*List<AttributeValueIndex> attributeValueIndexList = new ArrayList<>();
                //循环遍历赋值
                for (AlbumAttributeValue albumAttributeValue : albumAttributeValueVoList) {
                    //创建一个对象
                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                    //添加到集合
                    attributeValueIndexList.add(attributeValueIndex);
                }*/

                //  循环遍历;
                List<AttributeValueIndex> attributeValueIndexList = albumAttributeValueVoList.stream().map(albumAttributeValue -> {
                    //  创建一个对象
                    AttributeValueIndex attributeValueIndex = new AttributeValueIndex();
                    attributeValueIndex.setAttributeId(albumAttributeValue.getAttributeId());
                    attributeValueIndex.setValueId(albumAttributeValue.getValueId());
                    return attributeValueIndex;
                }).collect(Collectors.toList());
                //  添加属性与属性值集合；
                albumInfoIndex.setAttributeValueIndexList(attributeValueIndexList);
            }
            return albumInfo;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("查询专辑信息失败：{}", throwable.getMessage());
            return null;
        });


        //根据三级分类id查询视图
        //获取到上一个返回值结果并使用,因为查询三级id得到的返回值结果后边无人使用，所以选择thenAcceptAsync有参无返回值
        CompletableFuture<Integer> cateCompletableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            //远程调用根据三级分类id获取分类数据，因为视图是属于album库，而是service-album连的album库，所以还是search调用album
            Result<BaseCategoryView> categoryViewResult = categoryFeignClient.getBaseCategoryView(albumInfo.getCategory3Id());
            Assert.notNull(categoryViewResult, "查询分类数据失败");
            BaseCategoryView baseCategoryView = categoryViewResult.getData();
            Assert.notNull(baseCategoryView, "查询分类数据失败");
            //赋值，刚才的albuminfo中已经有三级分类id，所以不用再传，只传一级和二级分类id即可
            albumInfoIndex.setCategory1Id(baseCategoryView.getCategory1Id());
            albumInfoIndex.setCategory2Id(baseCategoryView.getCategory2Id());
            return 1;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("查询分类数据失败：{}", throwable.getMessage());
            return 0;
        });

        //查询作者信息
        CompletableFuture<Integer> userCompletableFuture = albumInfoCompletableFuture.thenApplyAsync(albumInfo -> {
            //远程调用
            Result<UserInfoVo> userInfoVoResult = userInfoFeignClient.getUserInfoVo(albumInfo.getUserId());
            Assert.notNull(userInfoVoResult, "查询作者信息失败");
            UserInfoVo userInfoVo = userInfoVoResult.getData();
            Assert.notNull(userInfoVo, "查询作者信息失败");
            albumInfoIndex.setAnnouncerName(userInfoVo.getNickname());
            return 1;
        }, threadPoolExecutor).exceptionally(throwable -> {
            log.error("查询分类数据失败：{}", throwable.getMessage());
            return 0;
        });

        //统计信息，根据专辑id查询专辑的统计数据
        albumInfoIndex.setPlayStatNum(new Random().nextInt(10000));
        albumInfoIndex.setSubscribeStatNum(new Random().nextInt(10000));
        albumInfoIndex.setBuyStatNum(new Random().nextInt(10000));
        albumInfoIndex.setCommentStatNum(new Random().nextInt(10000));
        //设置热度排名
        albumInfoIndex.setHotScore(Double.valueOf(new Random().nextInt(10000)));

        try {
            if(null==albumInfoCompletableFuture.get() || 0==cateCompletableFuture.get() ||0==userCompletableFuture.get()){
                //获取专辑失败
                throw new GuiguException(20001, "远程调用获取数据失败");

            }
        } catch (InterruptedException e) {
            throw new RuntimeException(e);
        } catch (ExecutionException e) {
            throw new RuntimeException(e);
        }
        //以上是使用异步编排设置的数据，要做数据汇总才能保存,join()方法会阻塞当前线程，直到所有这些异步任务都完成
        //CompletableFuture.allOf(albumInfoCompletableFuture,cateCompletableFuture,userCompletableFuture).join();

        //利用es的api，进行数据保存，albumInfoIndex-》es
        albumIndexRepository.save(albumInfoIndex);

        //上架提出库
        SuggestIndex suggestIndex = new SuggestIndex();
        suggestIndex.setId(UUID.randomUUID().toString().replaceAll("-",""));
        suggestIndex.setTitle(albumInfoIndex.getAlbumTitle());
        suggestIndex.setKeyword(new Completion(new String[]{albumInfoIndex.getAlbumTitle()}));
        suggestIndex.setKeywordPinyin(new Completion(new String[]{PinYinUtils.toHanyuPinyin(albumInfoIndex.getAlbumIntro())}));
        suggestIndex.setKeywordSequence(new Completion(new String[]{PinYinUtils.getFirstLetter(albumInfoIndex.getAlbumIntro())}));
        suggestIndexRepository.save(suggestIndex);
    }

    /**
     * 专辑下架
     * @param albumId
     */
    @Override
    public void lowerAlbum(Long albumId) {
        //利用es的api，进行数据删除
        albumIndexRepository.deleteById(albumId);

    }

    /**
     * 检索
     * @param albumIndexQuery
     * @return
     */
    @Override
    public AlbumSearchResponseVo search(AlbumIndexQuery albumIndexQuery) {

        //1.先获取到用户检索的dsl语句
        //第一个参数是SearchRequest可以封装各种的查询条件，最终就是dsl语句
        //第二个参数是查询完返回的类型
        SearchRequest searchRequest=this.queryBuildDsl(albumIndexQuery);
        SearchResponse<AlbumInfoIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(searchRequest, AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //赋值
        //AlbumSearchResponseVo albumSearchResponseVo =new AlbumSearchResponseVo();
        AlbumSearchResponseVo albumSearchResponseVo = this.parseResultData(searchResponse);
        albumSearchResponseVo.setTotal(searchResponse.hits().total().value());
        albumSearchResponseVo.setPageNo(albumIndexQuery.getPageNo());
        albumSearchResponseVo.setPageSize(albumIndexQuery.getPageSize());
        //  计算总页数
        Long totalPages = albumSearchResponseVo.getTotal()%albumIndexQuery.getPageSize()==0?albumSearchResponseVo.getTotal()/albumIndexQuery.getPageSize():albumSearchResponseVo.getTotal()/albumIndexQuery.getPageSize()+1;
        //Long totalPages = (albumSearchResponseVo.getTotal() + albumIndexQuery.getPageSize() - 1) / albumIndexQuery.getPageSize();
        albumSearchResponseVo.setTotalPages(totalPages);
        //返回值
        return albumSearchResponseVo;
    }

    //获取结果集+给结果集中的List<AlbumInfoIndexVo>赋值
    private AlbumSearchResponseVo parseResultData(SearchResponse<AlbumInfoIndex> searchResponse) {
        //返回值是AlbumSearchResponseVo，创建一个AlbumSearchResponseVo对象
        AlbumSearchResponseVo albumSearchResponseVo = new AlbumSearchResponseVo();
        //给AlbumSearchResponseVo类中的private List<AlbumInfoIndexVo> list = new ArrayList<>();赋值
        List<AlbumInfoIndexVo> list = new ArrayList<>();
        //循环遍历
        for (Hit<AlbumInfoIndex> hit : searchResponse.hits().hits()) {
            //从_source中取值
            AlbumInfoIndex albumInfoIndex = hit.source();
            AlbumInfoIndexVo albumInfoIndexVo = new AlbumInfoIndexVo();
            //属性赋值
            BeanUtils.copyProperties(albumInfoIndex,albumInfoIndexVo);
            list.add(albumInfoIndexVo);
        }
        albumSearchResponseVo.setList(list);
        return albumSearchResponseVo;
    }

    //获取到用户检索的dsl语句
    private SearchRequest queryBuildDsl(AlbumIndexQuery albumIndexQuery) {
        //  SearchRequest 本质就是 DSL 语句封装类;
        //  SearchRequest不能直接new，以为他的构造方法是私有的，无法直接new对象
        //  SearchRequest类中有个Builder方法，是SearchRequest类的默认无参构造
        SearchRequest.Builder searchRequestBuilder = new SearchRequest.Builder();
        BoolQuery.Builder boolQuery = new BoolQuery.Builder();
        //  与query并列的使用searchRequestBuilder，在内部查询的条件bool使用booleanQuery
        //  searchRequestBuilder：对应整个查询请求的顶级配置，负责构建所有「与查询条件并列」的参数（如高亮、分页、排序、索引指定等
        //  boolQuery：仅负责构建 query 字段内部的查询逻辑（如 must/should/filter 等条件组合），是查询的「核心条件部分」
        //指定索引库
        searchRequestBuilder.index("albuminfo");
        if(!StringUtils.isEmpty(albumIndexQuery.getKeyword())){
            //根据关键词查询
            //field.query()：通常用于全文搜索，会对输入进行分词处理，然后在指定字段的分词结果中进行匹配
            //field.value()：更常用于精确匹配，不会对输入进行分词，适合于 keyword 类型的字段或精确匹配场景
            boolQuery.should(s->s.match(m->m.field("albumTitle").query(albumIndexQuery.getKeyword())))
                    .should(s->s.match(m->m.field("albumIntro").query(albumIndexQuery.getKeyword())))
                    .should(s->s.match(m->m.field("announcerName").query(albumIndexQuery.getKeyword())));

            //  设置高亮;
            searchRequestBuilder.highlight(h -> h.fields("albumTitle"
                    ,
                    f -> f.preTags("<span style=color:red>")
                            .postTags("</span>")));

        }

        //根据三级分类id做检索
        if (!StringUtils.isEmpty(albumIndexQuery.getCategory1Id())) {
            //  根据一级分类Id过滤;
            boolQuery.filter(f -> f.term(t -> t.field("category1Id").value(albumIndexQuery.getCategory1Id())));
        }
        if (!StringUtils.isEmpty(albumIndexQuery.getCategory2Id())) {
            //  根据二级分类Id过滤;
            boolQuery.filter(f -> f.term(t -> t.field("category2Id").value(albumIndexQuery.getCategory2Id())));
        }
        if (!StringUtils.isEmpty(albumIndexQuery.getCategory3Id())) {
            //  根据三级分类Id过滤;
            boolQuery.filter(f -> f.term(t -> t.field("category3Id").value(albumIndexQuery.getCategory3Id())));
        }
        //经过两个检索入口后（关键词查询和分类id查询），用户还有可能根据属性值过滤
        //要先判断是否有属性值过滤，判断属性值是否为空
        List<String> attributeList = albumIndexQuery.getAttributeList();
        if(!CollectionUtils.isEmpty(attributeList)){
            for (String attribute : attributeList) {
                String[] split = attribute.split(":");
                if(null!=split && split.length==2){
                    boolQuery.filter(f->f.nested(n->n.path("attributeValueIndexList")
                            .query(q->q.bool(b->b.must(m->m.term(t->t.field("attributeValueIndexList.attributeId").value(split[0])))
                                    .must(m->m.term(t->t.field("attributeValueIndexList.valueId").value(split[1])))
                            ))
                    ));
                }
            }
        }
        //  分页；
        Integer from=(albumIndexQuery.getPageNo()-1)*albumIndexQuery.getPageSize();
        searchRequestBuilder.from(from);
        searchRequestBuilder.size(albumIndexQuery.getPageSize());

        //排序
        //要先判断是否有分页过滤
        String order = albumIndexQuery.getOrder();
        if(!StringUtils.isEmpty(order)){
            //有分页，// 1：综合排序 2：播放量 3：最近更新
            String[] split = order.split(":");
            if(null!=split && split.length==2){
                String field="";
                switch (split[0]){
                    case "1":
                        //综合排序
                        field = "hotScore";
                        break;
                    case "2":
                        //  播放量排序;
                        field = "playStatNum";
                        break;
                    case "3":
                        //  发布时间排序;
                        field = "createTime";
                }
                //  设置排序字段：
                String finalField = field;
                searchRequestBuilder.sort(s->s.field(f->f.field(finalField).order("asc".equals(split[1])? SortOrder.Asc:SortOrder.Desc)));
            }
        }

        searchRequestBuilder.query(boolQuery.build()._toQuery());
        SearchRequest searchRequest = searchRequestBuilder.build();
        //  打印DSL语句：
        System.out.println("DSL:\t" + searchRequest.toString());
        return searchRequest;
    }

    /**
     * 频道页数据
     * @param category1Id
     * @return
     */
    @Override
    public List<Map<String, Object>> channel(Long category1Id) {
        //  前七位的数据相当于一个list，list中存了七个map，每个map中存储了两个键值对
        //  一个是三级分类的对象("baseCategory3",三级分类对象),一个是三级分类下的专辑列表("list",专辑集合列表)
        //  key都是字符串，value是两个类型，所以直接使用object

        //用一级分类id查到三级分类列表
        Result<List<BaseCategory3>> baseCategory3ListResult = categoryFeignClient.findTopBaseCategory3(category1Id);
        //判断
        Assert.notNull(baseCategory3ListResult, "查询三级分类集合失败");
        //三级分类列表,findTopBaseCategory3 limit 7 --> 有七条数据; 1001 ... 1007
        List<BaseCategory3> baseCategory3List = baseCategory3ListResult.getData();
        //判断
        Assert.notNull(baseCategory3List, "查询三级分类集合失败");
        //通过这个集合获取到三级分类Id集合（1001......1007）
        List<Long> category3IdList = baseCategory3List.stream().map(BaseCategory3::getId).collect(Collectors.toList());
        //通过三级分类Id查找三级分类对象; category3Id -> BaseCategory3
        //将category3IdList转换为map，map集合中key=三级分类id，value=三级分类对象
        //列表转map：后续可以通过category3Map.get(id)快速根据 ID 获取对应的三级分类对象，避免重复遍历列表查询
        Map<Long, BaseCategory3> category3Map = baseCategory3List.stream().collect(Collectors.toMap(BaseCategory3::getId, Function.identity()));

        ////根据三级分类id去远程调用album获取对应的专辑信息太麻烦，所以可以在elasticsearch中取数据
        //elasticsearch中三级分类id的集合需要的是FieldValue类型，所以此处需要将category3IdList转化为fieldValue类型,List<Long>-->List<FieldValue>
        List<FieldValue> fieldValueList = baseCategory3List.stream().map(baseCategory3 -> FieldValue.of(baseCategory3.getId())).collect(Collectors.toList());
        // 专辑集合列表
        SearchResponse<AlbumInfoIndex> searchResponse=null;
        try {
            searchResponse = elasticsearchClient.search(s->s.index("albuminfo")
                            .query(q->q.terms(t->t.field("category3Id")
                                    .terms(t1->t1.value(fieldValueList))))
                            .aggregations("agg_category3Id",a->a.terms(t2->t2.field("category3Id"))
                                    .aggregations("topSixHotScoreAgg", a1->a1.topHits(t1->t1.size(6)
                                            .sort(s1 -> s1.field(f->f.field("hotScore").order(SortOrder.Desc)))))
                            )
                    , AlbumInfoIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }

        //获取结果组装数据
        List<Map<String, Object>> list = searchResponse.aggregations().get("agg_category3Id").lterms().buckets().array().stream().map(bucket -> {
            HashMap<String, Object> map = new HashMap<>();
            //获取key（三级分类id）
            long caregoery3Id = bucket.key();
            //获取三级分类对象
            map.put("baseCategory3", category3Map.get(caregoery3Id));

            //  获取专辑集合列表;
            Aggregate topSixHotScoreAgg = bucket.aggregations().get("topSixHotScoreAgg");
            List<AlbumInfoIndex> albumInfoIndexList = topSixHotScoreAgg.topHits().hits().hits().stream().map(hit -> JSON.parseObject(hit.source().toString(), AlbumInfoIndex.class)).collect(Collectors.toList());
            //将专辑列表存入map
            map.put("list",albumInfoIndexList);
            //返回map
            return map;
        }).collect(Collectors.toList());
        return list;
    }

    /**
     * 自动补全
     * @param keyword
     * @return
     */
    @Override
    public List<String> completeSuggest(String keyword) {
        //  Java 动态生成dsl 语句.
        SearchResponse<SuggestIndex> searchResponse = null;
        try {
            searchResponse = elasticsearchClient.search(
                s -> s.index("suggestinfo")
                        .suggest(s1 -> s1.suggesters("suggest_keyword"
                                                ,
                                                f -> f.prefix(keyword).completion(c -> c.field("keyword")
                                                        .skipDuplicates(true)
                                                        .fuzzy(f1 -> f1.fuzziness("auto"))
                                                        .size(10))
                                        )
                                        .suggesters("suggest_keywordPinyin"
                                                ,
                                                f -> f.prefix(keyword).completion(c -> c.field("keywordPinyin")
                                                        .skipDuplicates(true)
                                                        .fuzzy(f1 -> f1.fuzziness("auto"))
                                                        .size(10))
                                        )
                                        .suggesters("suggest_keywordSequence"
                                                ,
                                                f -> f.prefix(keyword).completion(c -> c.field("keywordSequence")
                                                        .skipDuplicates(true)
                                                        .fuzzy(f1 -> f1.fuzziness("auto"))
                                                        .size(10))
                                        )
                        )
                , SuggestIndex.class);
        } catch (IOException e) {
            throw new RuntimeException(e);
        }
        //  获取到查询结果集,调用 getSuggestList 方法提取每个建议器的结果，并合并到一个列表中返回
        List<String> list = new ArrayList<>();
        //  定义一个方法：
        list.addAll(this.getSuggestList(searchResponse, "suggest_keyword"));
        list.addAll(this.getSuggestList(searchResponse, "suggest_keywordPinyin"));
        list.addAll(this.getSuggestList(searchResponse, "suggest_keywordSequence"));
        //  返回数据
        return list;
    }

    /**
     * 获取提出库数据，从 Elasticsearch 的建议查询响应中，提取指定建议器（通过 keyword 参数标识）的补全结果，并转换为字符串列表返回
     *
     * @param searchResponse
     * @param keyword
     * @return
     */
    private Collection<String> getSuggestList(SearchResponse<SuggestIndex> searchResponse, String keyword) {
        //  根据key 获取结果列表
        List<Suggestion<SuggestIndex>> suggestionList = searchResponse.suggest().get(keyword);
        //遍历结果，从 completion 的options 中提取 source().getTitle()（补全文本），转换为字符串列表返回
        if (!CollectionUtils.isEmpty(suggestionList)) {
            for (Suggestion<SuggestIndex> suggestIndexSuggestion : suggestionList) {
//            List<String> list = suggestIndexSuggestion.completion().options().stream().map(option -> {
//                String title = option.source().getTitle();
//                return title;
//            }).collect(Collectors.toList());
                return suggestIndexSuggestion.completion().options().stream().map(option -> option.source().getTitle()).collect(Collectors.toList());
            }
        }

        //  返回数据
        return new ArrayList<>();
    }

    /**
     * 更新排行榜
     * 1. 先查询所有一级分类数据
     * 2. 按照每个维度来统计排行 热度:hotScore、播放量:playStatNum、订阅量:subscribeStatNum、购买量:buyStatNum、评论数:commentStatNum
     * 3. 从es中重新获取排序之后的专辑信息，并写入缓存中
     */
    @Override
    public void updateLatelyAlbumRanking() {
        //查询所有一级分类数据，select * from base_category1;
        Result<List<BaseCategory1>> baseCategory1Result = categoryFeignClient.findAllCategory1();
        Assert.notNull(baseCategory1Result, "查询一级分类数据失败");
        List<BaseCategory1> baseCategory1List = baseCategory1Result.getData();
        Assert.notNull(baseCategory1List, "查询一级分类数据失败");

        //获取排序的维度；
        String[] split = rankingDimensionArray.split(",");
        //循环变量集合 调用es客户端生成dsl语句并执行获取结果集并放入缓存！hash数据结构，hset key field value
        for (BaseCategory1 baseCategory1 : baseCategory1List) {
            Long baseCategory1Id = baseCategory1.getId();
            for (String rangking : split) {
                SearchResponse<AlbumInfoIndex> searchResponse=null;
                //  调用es api，获取数据（value）
                try {
                    searchResponse = elasticsearchClient.search(s -> s.index("albuminfo")
                                    .query(q -> q.term(t -> t.field("category1Id").value(baseCategory1Id)))
                                    .sort(s1 -> s1.field(f -> f.field(rangking).order(SortOrder.Desc)))
                            , AlbumInfoIndex.class);
                } catch (IOException e) {
                    throw new RuntimeException(e);
                }

                //获取集合列表
                List<AlbumInfoIndex> albumInfoIndexList = searchResponse.hits().hits().stream().map(hit -> hit.source()).collect(Collectors.toList());

                //存到缓存中
                redisTemplate.opsForHash().put(RedisConstant.RANKING_KEY_PREFIX+baseCategory1Id,rangking,albumInfoIndexList);
            }
        }
    }

    /**
     * 获取排行榜
     * @param category1Id
     * @param rangking
     * @return
     */
    @Override
    public List<AlbumInfoIndex> findRankingList(Long category1Id, String rangking) {
        //从缓存中取排行榜
        List<AlbumInfoIndex> albumInfoIndexlist = (List<AlbumInfoIndex>) redisTemplate.opsForHash().get(RedisConstant.RANKING_KEY_PREFIX+category1Id, rangking);
        return albumInfoIndexlist;
    }
}
